using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml;

namespace ThatsMeSDK.Core
{
	public static class ServiceHandler
	{
		/// <summary>
		/// Loads Data from the Web API
		/// </summary>
		internal static IDataProvider dataloader;
		
		/// <summary>
		/// ThatsMe Api Url Constant
		/// </summary>
		private const string ApiUrl = "http://thats-me.ch/api/";

        //Events
        public static event GetMemberCompleted GetMemberByNickCompleted;
        public static event GetMemberCompleted GetMemberByIdCompleted;
        public static event LoadPicturesCompleted GetNewPicsCompleted;
        public static event AuthenticateCompleted AuthenticateCompleted;
        public static event CountMessagesCompleted CountMessagesCompleted;
        public static event LoadMessageCompleted GetMessageCompleted;
        public static event LoadMessagesCompleted GetMessagesCompleted;
        public static event LoadUserPicCategoriesCompleted GetUserPicCategoriesCompleted;
        public static event LoadPicturesCompleted GetUserPicsCompleted;
        public static event LoadBooleanCompleted SendMessageCompleted;

		/// <summary>
		/// Initialize the SDK Core
		/// </summary>
		/// <param name="format">Api Data Format</param>
		public static void Initialize(DataFormat format)
		{
			if(format == DataFormat.Xml)
			{
				dataloader = new XmlDataProvider();
			}
			else if(format == DataFormat.Json)
			{
                dataloader = null;
			}

            dataloader.LoadDataCompleted += new LoadDataCompleted(dataloader_LoadDataCompleted);
		}

        /// <summary>
        /// Pass event to own event //No event passing -> just start the function
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void dataloader_LoadDataCompleted(object sender, LoadDataCompletedEventArgs e, ServiceHandlerMethod c, object Tag)
        {
            //Start Called Funktion Event Handler
            c.Invoke(sender, new LoadDataCompletedEventArgs(ServiceFunctions.CheckData(e.Data), e.Tag));
        }
		
		/// <summary>
		/// Load informations about a member by his nickname.
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>Filled Member Object</returns>
		public static void GetMemberByNick(string nick)
		{
			//Fill API Data
			ApiCall call = new ApiCall("getMemberByNick");
			call.Parameter.Add("nick", nick);
            call.EventProcessor = GetMemberByNickHandler;
			
			//Send Request
			CallNativeApiFunction(call);
		}

        /// <summary>
        /// Handler for "GetMemberByNick"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void GetMemberByNickHandler(object sender, EventArgs e)
        {
            //Handle Request
            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e; //cast it into the real EventArg
            XmlDocument memberdata = args.Data;
            Member tempMember = ServiceFunctions.FillMemberData(memberdata.SelectSingleNode("member"));
            GetMemberByNickCompleted(sender, new GetMemberCompletedEventArgs(tempMember));
        }

		/// <summary>
		/// Load informations about a member by his id.
		/// </summary>
		/// <param name="nick">Id of the member</param>
		/// <returns>Filled Member Object</returns>
		public static void GetMemberById(int id)
		{
			//Fill API Data
			ApiCall call = new ApiCall("getMemberById");
			call.Parameter.Add("id", id.ToString());
            call.EventProcessor = GetMemberByIdHandler;
			
			//Handle Request
			CallNativeApiFunction(call);
		}

        /// <summary>
        /// Handler for "GetMemberById"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void GetMemberByIdHandler(object sender, EventArgs e)
        {
            //Handle Request
            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e; //cast it into the real EventArg
            XmlDocument memberdata = args.Data;
            Member tempMember = ServiceFunctions.FillMemberData(memberdata.SelectSingleNode("member"));
            GetMemberByIdCompleted(sender, new GetMemberCompletedEventArgs(tempMember));
        }
		
		/// <summary>
		/// Returns the news picture fo TM
		/// </summary>
		/// <param name="limit">Limit the amount of pictures</param>
		/// <returns>A list of Pictures</returns>
		public static void GetNewPics(int limit)
		{	
			//Fill API Data
			ApiCall call = new ApiCall("getNewPics");
			call.Parameter.Add("limit", limit.ToString());
            call.EventProcessor = GetNewPicsHandler;

            //Send Request
            CallNativeApiFunction(call);
		}

        private static void GetNewPicsHandler(object sender, EventArgs e)
        {
            List<GalleryPicture> pictures = new List<GalleryPicture>();

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument picturedata = args.Data;
            XmlNode picturesNode = picturedata.SelectSingleNode("pics");

            foreach (XmlNode picNode in picturesNode.ChildNodes)
            {
                pictures.Add(ServiceFunctions.FillGalleryPictureData(picNode));
            }

            GetNewPicsCompleted(sender, new LoadPicturesCompletedEventArgs(pictures));
        }

		/// <summary>
		/// authenticate a member and creates a new session
		/// </summary>
		/// <param name="nick">nickname of the user</param>
		/// <param name="passwordhash">md5 claculated password hash of the user</param>
		/// <returns>token if login was ok
		/// null if login failed</returns>
		public static void Authenticate(string nick, string passwordhash)
		{	
			//Call API Data
			ApiCall call = new ApiCall("authenticate");
			call.Parameter.Add("nick", nick);
			call.Parameter.Add("pass", passwordhash);
            call.EventProcessor = AuthenticateHandler;

            CallNativeApiFunction(call);
		}

        private static void AuthenticateHandler(object sender, EventArgs e)
        {
            string token;

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument logindata = args.Data;
            XmlNode memberNode = logindata.SelectSingleNode("member");

            if (memberNode.ChildNodes.Count > 0)
            {
                //Login was Ok
                token = memberNode.SelectSingleNode("token").InnerText;
            }
            else
            {
                token = null;
            }

            AuthenticateCompleted(sender, new AuthenticateCompletedEventArgs(token));
        }
		
		/// <summary>
		/// Returns a MessageCount Object which contains all data about the amount of messages
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <returns>MessageCount Object with the data, on error null</returns>
		public static void CountMessages(string token)
		{		
			//Fill API Data
			ApiCall call = new ApiCall("countMessages");
			call.Parameter.Add("token", token);
            call.EventProcessor = CountMessagesHandler;

            CallNativeApiFunction(call);
		}

        private static void CountMessagesHandler(object sender, EventArgs e)
        {
            MessageCount counter = null;

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument counterdata = args.Data;

            if (!ServiceFunctions.CheckAuthentificationError(counterdata))
            {
                XmlNode countNode = counterdata.SelectSingleNode("count");
                XmlNode unreadNode = countNode.SelectSingleNode("unread");
                XmlNode inboxNode = countNode.SelectSingleNode("inbox");
                XmlNode outboxNode = countNode.SelectSingleNode("outbox");

                counter = new MessageCount();

                //Fill Data
                counter.Unread = new MessageCount.MessageCounter(
                    Convert.ToInt32(unreadNode.SelectSingleNode("messages").InnerText),
                    Convert.ToInt32(unreadNode.SelectSingleNode("notifications").InnerText),
                    Convert.ToInt32(unreadNode.SelectSingleNode("total").InnerText));

                counter.Inbox = new MessageCount.MessageCounter(
                    Convert.ToInt32(inboxNode.SelectSingleNode("messages").InnerText),
                    Convert.ToInt32(inboxNode.SelectSingleNode("notifications").InnerText),
                    Convert.ToInt32(inboxNode.SelectSingleNode("total").InnerText));

                counter.Outbox = new MessageCount.MessageCounter(
                    Convert.ToInt32(outboxNode.SelectSingleNode("messages").InnerText),
                    Convert.ToInt32(outboxNode.SelectSingleNode("notifications").InnerText),
                    Convert.ToInt32(outboxNode.SelectSingleNode("total").InnerText));
            }

            CountMessagesCompleted(sender, new CountMessagesCompletedEventArgs(counter));
        }
		
		/// <summary>
		/// Loads a message by a message id
		/// </summary>
		/// <param name="id">Id of the message</param>
		/// <param name="token">User authentification token</param>
		/// <returns>Filled Message Object</returns>
		public static void GetMessage(int id, string token)
		{	
			//Fill API Data
			ApiCall call = new ApiCall("getMessage");
			call.Parameter.Add("token", token);
			call.Parameter.Add("id", id.ToString());
            call.EventProcessor = GetMessageHandler;

            CallNativeApiFunction(call);
		}
		/// <summary>
		/// Loads a message by a message header
		/// </summary>
		/// <param name="messageHead">Message header of the message</param>
		/// <param name="token">User authentification token</param>
		/// <returns>Filled Message Object</returns>
		public static void GetMessage(MessageHeader messageHead, string token)
		{
			GetMessage(messageHead.Id, token);
		}

        private static void GetMessageHandler(object sender, EventArgs e)
        {
            Message message = null;

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument messageData = args.Data;

            if (!ServiceFunctions.CheckAuthentificationError(messageData))
            {
                message = ServiceFunctions.FillMessageData(messageData.SelectSingleNode("message"));
            }

            GetMessageCompleted(sender, new LoadMessageCompletedEventArgs(message));
        }
		
		/// <summary>
		/// Loads a list of messages
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <param name="start">Start of the message list</param>
		/// <param name="limit">List limit of item count</param>
		/// <param name="only">Message Type you want to load</param>
		/// <param name="fullMessage">Set true if you want to download full message</param>
		/// <returns></returns>
		public static void GetMessages(string token, int start, int limit, MessageType only, bool fullMessage)
		{
			
			//Fill API Data
			ApiCall call = new ApiCall("getMessages");
			call.Parameter.Add("token", token);
			call.Parameter.Add("start", start.ToString());
			call.Parameter.Add("limit", limit.ToString());
			call.Parameter.Add("full", Convert.ToInt32(fullMessage).ToString());
            call.Tag = fullMessage;
            call.EventProcessor = GetMessagesHandler;
			
			//Check Message Type
			if (only != MessageType.Both)
			{
				call.Parameter.Add("only", only.ToString().ToLower());
			}

            CallNativeApiFunction(call);
		}

        private static void GetMessagesHandler(object sender, EventArgs e)
        {
            List<Message> messages = null;

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument messagesData = args.Data;
            Boolean fullMessage = (bool)args.Tag;

            messages = new List<Message>();
            XmlNode messagesNode = messagesData.SelectSingleNode("messages");

            foreach (XmlNode messageNode in messagesNode.ChildNodes)
            {
                Message tempMessage = new Message();

                if (fullMessage)
                {
                    tempMessage = ServiceFunctions.FillMessageData(messageNode);
                }
                else
                {
                    tempMessage.Header = ServiceFunctions.FillMessageHeaderData(messageNode);
                }
                messages.Add(tempMessage);
            }

            GetMessagesCompleted(sender, new LoadMessagesCompletedEventArgs(messages));
        }
		
		/// <summary>
		/// Sends a message to the specified member (Doesn't work because of "Get" Request)
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <param name="body">Message Body</param>
		/// <param name="to">Message Recipient</param>
		/// <param name="subject">Message Subject</param>
		/// <returns>Boolean if success</returns>
		public static void SendMessage (string token, string body, string to, string subject)
		{
			//Fill API Data
			ApiCall call = new ApiCall("sendMessage");
			call.Parameter.Add("token", token);
			call.Parameter.Add("body", body);
			call.Parameter.Add("to", to);
			call.Parameter.Add("subject", subject);
            call.EventProcessor = SendMessageHandler;

            CallNativeApiFunction(call);
		}

        public static void SendMessageHandler(object sender, EventArgs e)
        {
            bool retValue = false;

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument messageData = args.Data;

            XmlNode errorNode = messageData.SelectSingleNode("error");

            if (errorNode.InnerText == string.Empty)
            {
                retValue = true;
            }
            else
            {
                retValue = false;
            }

            SendMessageCompleted(sender, new LoadBooleanCompletedEventArgs(retValue));
        }
		
		/// <summary>
		/// Loads the gallery categories of a member
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>List with GalleryCategories</returns>
		public static void GetUserPicCategories(string nick)
		{	
			//Fill API Data
			ApiCall call = new ApiCall("getUserPicCategories");
			call.Parameter.Add("user", nick);
            call.EventProcessor = GetUserPicCategoriesHandler;
            call.Tag = nick;

            //Download
            CallNativeApiFunction(call);
		}

        private static void GetUserPicCategoriesHandler(object sender, EventArgs e)
        {
            List<GalleryCategory> categories = new List<GalleryCategory>();
            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            string nick = (string)args.Tag;

            XmlDocument categoriesData = args.Data; 

            XmlNode categoriesNode = categoriesData.SelectSingleNode("categories");

            foreach (XmlNode categoryNode in categoriesNode)
            {
                categories.Add(ServiceFunctions.FilleGalleryCategoryData(nick, categoryNode));
            }

            GetUserPicCategoriesCompleted(sender, new LoadUserPicCategoriesCompletedEventArgs(categories));
        }
		
		/// <summary>
		/// Loads all Pictures of a member by a gallery id
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <param name="galleryid">Gallery Id</param>
		/// <returns>List with Gallery Pictures</returns>
		public static void GetUserPics(string nick, int galleryid)
		{			
			//Fill API Data
			ApiCall call = new ApiCall("getUserPics");
			call.Parameter.Add("user", nick);
			call.Parameter.Add("category", galleryid.ToString());
            call.EventProcessor = GetUserPicsHandler;

            CallNativeApiFunction(call);
		}

        private static void GetUserPicsHandler(object sender, EventArgs e)
        {
            List<GalleryPicture> pictures = new List<GalleryPicture>();

            LoadDataCompletedEventArgs args = (LoadDataCompletedEventArgs)e;
            XmlDocument picturesData = args.Data;

            XmlNode picturesNode = picturesData.SelectSingleNode("pics");

            foreach (XmlNode pictureNode in picturesNode)
            {
                pictures.Add(ServiceFunctions.FillGalleryPictureData(pictureNode));
            }

            GetUserPicsCompleted(sender, new LoadPicturesCompletedEventArgs(pictures));
        }
		
		/// <summary>
		/// Loads all Pictures of a member
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>List with Gallery Pictures</returns>
		public static void GetUserPics(string nick)
		{
			GetUserPics(nick, -1);
		}

//        public static  Member LoadCompletlyFilledMember(int id)
//        {
//#warning Member returns null! Please make it asynchron
//            return null;//LoadCompletlyFilledMember(GetMemberById(id).Nick);
//        }

//        public static  Member LoadCompletlyFilledMember(string nick)
//        {
//#warning Member returns null! Please make it asynchron
//            Member member = null; //GetMemberByNick(nick);
//            //Here comes all the code which is needed to load a completly user.
//            //Not like GetMemberByNick which filled only a couple of fields.    For example: Music,Friends,etc.

//            return member;
//        }

//        public static Dashboard UpdateDashboardData(Dashboard dashboard)
//        {
//#warning Pictures returns null! Please make it asynchron
//            dashboard.NewPictures = null; //GetNewPics(1);
//            //Here comes all Code to load the Dashboard completly like GetNewBlogs, etc.

//            return null;
//        }
		
		/// <summary>
		/// Calls the specified API Function
		/// </summary>
		/// <param name="functionName">Name of the API Function</param>
		/// <param name="parameter">Parameter String</param>
		/// <returns>Data as XmlDocument</returns>
		internal static void CallNativeApiFunction(string functionName, string parameter, ServiceHandlerMethod c, object Tag)
		{
			ServiceFunctions.CheckServiceHandlerReadyState();
            dataloader.LoadData(new Uri(ApiUrl + functionName + "?" + parameter), c, Tag);
		}
		
		/// <summary>
		/// Calls the specified API Function
		/// </summary>
		/// <param name="apiCall">API Call Object</param>
		/// <returns>Data as XmlDocument</returns>
		internal static void CallNativeApiFunction(ApiCall apiCall)
		{
			string parameter = "";
			bool isFirstItem = true;
			string spacer = "";
			
			foreach(KeyValuePair<string, string> paramEntry in apiCall.Parameter)
			{
				parameter += spacer + paramEntry.Key + "=" + paramEntry.Value;
				
				if (isFirstItem)
				{
					isFirstItem = false;
					spacer += "&";
				}
			}
			
			CallNativeApiFunction(apiCall.FunctionName, parameter, apiCall.EventProcessor, apiCall.Tag);
		}
		
		/// <summary>
		/// Calculates an MD5 hash of a text (string)
		/// </summary>
		/// <param name="Input">Text as string</param>
		/// <returns>Calculated Hash as string</returns>
		public static string CalculateMD5(string Input)
		{
			MD5CryptoServiceProvider CryptoService;
			CryptoService = new MD5CryptoServiceProvider();

			byte[] InputBytes = UTF8Encoding.UTF8.GetBytes(Input);
			InputBytes = CryptoService.ComputeHash(InputBytes);
			return BitConverter.ToString(InputBytes).Replace("-", "").ToLower();
		}
	}
}
